package com.xinhe.web.service;

import com.alibaba.fastjson.JSONObject;
import com.xinhe.web.common.BusinessException;
import com.xinhe.web.common.Result;
import com.xinhe.web.entity.ValueConfig;
import com.xinhe.web.entity.WeixinConfig;
import com.xinhe.web.entity.WeixinTrade;
import com.xinhe.web.entity.WeixinUser;
import com.xinhe.web.reposity.ValueConfigRepository;
import com.xinhe.web.reposity.WeixinConfigRepository;
import com.xinhe.web.reposity.WeixinTradeRepository;
import com.xinhe.web.reposity.WeixinUserRepository;
import com.xinhe.web.session.UserSessionManager;
import com.xinhe.web.util.Snowflake;
import com.xinhe.web.util.Tools;
import com.xinhe.web.weixin.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Service
public class WeixinTradeService {

    private static final String SUCCESS = "success";

    private static final String SUCCESS_RETURN = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

    private static final String FAIL_RETURN = "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";

   /* @Autowired
    private IUserSessionManager userSessionManager;

    @Autowired
    private IValueConfigCache valueConfigCache;

    @Autowired
    private IWeixinTradeDao weixinTradeDao;
    @Autowired
    private IWeixinConfigDao weixinConfigDao;
    @Autowired
    private IWeixinUserDao weixinUserDao;
    @Autowired
    private WeixinNotifyUtil weixinNotifyUtil;*/
    @Autowired
    private HttpServletRequest request;
    @Autowired
    WeixinNotifyUtil weixinNotifyUtil;
    @Autowired
    WeixinConfigRepository weixinConfigRepository;
    @Autowired
    WeixinUserRepository weixinUserRepository;
    @Autowired
    WeixinTradeRepository weixinTradeRepository;
    @Autowired
    private UserSessionManager userSessionManager;
    @Autowired
    ValueConfigRepository valueConfigRepository;


    @Transactional(rollbackFor = {Exception.class})
    public Result requestWeixinPayParam(WeixinPayParamModel model) {
        WeixinAppPayParamView res = null;
        try {
            Map<String, String> params = model.getParams();
            Long userId = model.getUserId();
            //附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
            Long tradeId = Snowflake.generateId();
            //查询公众号appId和mchId
            WeixinConfig weixinConfig = weixinConfigRepository.findById(550617216979042304L).get();
            //小程序或者app或者公众号的appid
            params.put("appid", weixinConfig.getAppId());
            //微信支付分配的商户号
            params.put("mch_id", weixinConfig.getMchId());
            //随机字符串，长度要求在32位以内。推荐随机数生成算法
            params.put("nonce_str", WXPayUtil.generateNonceStr());
            //商户订单号，同一个商户号下唯一。
            params.put("out_trade_no", Snowflake.generateId().toString());
            //商品描述
            params.put("body", model.getBody());
            //公众号支付的话openid必须传
            if ("JSAPI".equals(model.getTradeType())) {
                String openId = model.getOpenId();
                if (StringUtils.isBlank(openId)) {
                    WeixinUser weixinUser = this.weixinUserRepository.findOneByConfigIdAndUserId(550617216979042304L, userId);
                    openId = weixinUser.getOpenid();
                }
                //trade_type=JSAPI时（即JSAPI支付），此参数必传，此参数为微信用户在商户对应appid下的唯一标识。openid如何获取，可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid，再调用【企业号userid转openid接口】进行转换
                params.put("openid", openId);
            }
            //订单失效时间，格式为yyyyMMddHHmmss，如2009年12月27日9点10分10秒表示为20091227091010。
            if (model.getTimeExpire() != null) {
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
                params.put("time_expire", dateFormat.format(model.getTimeExpire()));
            }
            //订单金额 微信那边单位是分，所以要x100
            Integer payMoney = Integer.valueOf(model.getFee().multiply(BigDecimal.valueOf(100L)).setScale(1, 4).intValue());
            params.put("total_fee", payMoney.toString());
            //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。 公网域名必须为https，如果是走专线接入，使用专线NAT IP或者私有回调域名可使用http
            //https://xhyp.quexiaoer.cn/api/adoptedTreeOrder/responseJsapiPay/550617216979042304
            //params.put("notify_url", convertNotifyUrl(model.getNotifyUrl(), new String[] { weixinConfig.getId().toString() }));
            params.put("notify_url", convertNotifyUrl(model.getNotifyUrl(),""));
            //交易类型 JSAPI -JSAPI支付 NATIVE -Native支付 APP -APP支付
            params.put("trade_type", model.getTradeType());
            //附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
            params.put("attach", tradeId.toString());
            //终端IP
            params.put("spbill_create_ip", Tools.getIpAddr(this.request));
            //调用微信接口
            String respXml = WXPayUtil.requestUnifiedorder(params, weixinConfig);
            log.info("weixin pay response xml: {}", respXml);
            if (StringUtils.isBlank(respXml)) {
                throw new BusinessException(3001,"微信请求超时");
            }
            Map<String, String> respMap = WXPayUtil.xmlToMap(respXml);
            String return_code = respMap.get("return_code");
            String result_code = respMap.get("result_code");
            String sign = respMap.get("sign");
            //生成签名和微信返回的对比签名
            String validSign = WXPayUtil.generateSignature(respMap, weixinConfig.getPayKey());
            if (!"success".equalsIgnoreCase(return_code)) {
                res = this.failure(respMap.get("return_msg"));
            } else if (!"success".equalsIgnoreCase(result_code)) {
                res = this.failure(respMap.get("err_code_des"));
            } else if (validSign.equals(sign)) {//验证签名
                res = this.success(respMap, weixinConfig);
            } else {
                res = this.failure("未通过微信校验");
            }
            WeixinTrade trade = new WeixinTrade();
            trade.setConfigId(550617216979042304L);
            trade.setDataId(model.getDataId());
            trade.setDataType(model.getDataType());
            trade.setFee(model.getFee());
            trade.setId(tradeId);
            trade.setExtendParams(JSONObject.toJSONString(model.getExtendParams()));
            trade.setRequestInfo(JSONObject.toJSONString(params));
            trade.setResponseInfo(JSONObject.toJSONString(respMap));
            trade.setTradeStatus(Integer.valueOf(1));
            trade.setTradeType(Integer.valueOf(1));
            trade.setUserId(userId);
            this.weixinTradeRepository.save(trade);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(2001,e.getMessage());
        }
        return Result.ok(res);
    }

    public WeixinAppPayParamView success(Map<String, String> response, WeixinConfig weixinConfig) {
        WeixinAppPayParamView payParam = new WeixinAppPayParamView();
        String nonceStr = response.get("nonce_str");
        String prepayId = response.get("prepay_id");
        String appId = response.get("appid");
        String paypackage = "Sign=WXPay";
        String timeStamp = (new Date()).getTime() + "";
        Map<String, String> signMap = new HashMap<>();
        signMap.put("appid", appId);
        signMap.put("partnerid", weixinConfig.getMchId());
        signMap.put("prepayid", prepayId);
        signMap.put("package", paypackage);
        signMap.put("noncestr", nonceStr);
        signMap.put("timestamp", timeStamp);
        try {
            String paySign = WXPayUtil.generateSignature(signMap, weixinConfig.getPayKey());
            payParam.setPaySign(paySign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        payParam.setAppId(appId);
        payParam.setMchId(weixinConfig.getMchId());
        payParam.setPrepayId(prepayId);
        payParam.setPaypackage(paypackage);
        payParam.setNonceStr(nonceStr);
        payParam.setTimeStamp(timeStamp);
        return payParam;
    }

    public WeixinAppPayParamView failure(String error) {
        WeixinAppPayParamView temp = new WeixinAppPayParamView();
        temp.setErroMsg(error);
        return temp;
    }


    @Transactional(rollbackFor = {Exception.class})
    public Result<WeixinPayNotifyView> getWeixinPayNotifyDataAndValidateAndUpdateWeixinTrade() {
        Map<String, String> notifyData = this.weixinNotifyUtil.getNotifyData();
        Long tradeId = Long.valueOf(notifyData.get("attach"));
        WeixinTrade trade = this.weixinTradeRepository.findById(tradeId).get();
        WeixinConfig weixinConfig = this.weixinConfigRepository.findById(trade.getConfigId()).get();
        //微信支付系统生成的订单号。
        String weixinTradeId = notifyData.get("transaction_id");
        //交易完成
        if (2 == trade.getTradeStatus().intValue()) {
            return Result.ok(null,"已经回调成功");
        }
        Boolean valid = false;
        try {
            //验证签名
            valid = WXPayUtil.isSignatureValid(notifyData, weixinConfig.getPayKey());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (valid) {
            trade.setTradeStatus(Integer.valueOf(2));
            trade.setWeixinTradeId(weixinTradeId);
            trade.setNotifyInfo(JSONObject.toJSONString(notifyData));
            this.weixinTradeRepository.saveAndFlush(trade);
            WeixinPayNotifyView view = new WeixinPayNotifyView();
            view.setNotifyData(notifyData);
            view.setWeixinTrade(trade);
            return Result.ok(view,"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
        } else {
            return Result.failed(2200,"不合法的请求信息<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
        }
    }


    @Transactional(rollbackFor = {Exception.class})
    public Result requestWeixinRefund(WeixinRefundModel model) {
        try {
            WeixinTrade trade = (WeixinTrade)this.weixinTradeRepository.findById(model.getTradeId()).get();
            Long refundId = Snowflake.generateId();
            Map<String, String> params = model.getParams();
            WeixinConfig weixinConfig = (WeixinConfig)this.weixinConfigRepository.findById(trade.getConfigId()).get();
            params.put("appid", weixinConfig.getAppId());
            params.put("mch_id", weixinConfig.getMchId());
            params.put("nonce_str", WXPayUtil.generateNonceStr());
            params.put("notify_url", convertNotifyUrl(model.getNotifyUrl(), new String[] { weixinConfig.getId().toString() }));
            params.put("transaction_id", trade.getWeixinTradeId());
            params.put("out_refund_no", refundId.toString());
            Integer payMoney = Integer.valueOf(trade.getFee().multiply(BigDecimal.valueOf(100L)).setScale(1, 4).intValue());
            Integer refundFee = payMoney;
            if (null != model.getFee()) {
                refundFee = Integer.valueOf(model.getFee().multiply(BigDecimal.valueOf(100L)).setScale(1, 4).intValue());
            }
            params.put("total_fee", payMoney.toString());
            params.put("refund_fee", refundFee.toString());
            params.put("refund_desc", model.getBody());
            String respXml = WXPayUtil.requestRefund(params, weixinConfig);
            if (StringUtils.isBlank(respXml)) {
                return Result.failed(4001,"微信请求失败");
            }
            Map<String, String> respMap = WXPayUtil.xmlToMap(respXml);
            String returnCode = respMap.get("return_code");
            String resultCode = respMap.get("result_code");
            if (!"success".equalsIgnoreCase(returnCode)) {
                return Result.failed(4002,respMap.get("return_msg"));
            } else if (!"success".equalsIgnoreCase(resultCode)) {
                return Result.failed(4003,respMap.get("err_code_des"));
            }
            WeixinTrade refund = new WeixinTrade();
            refund.setId(refundId);
            refund.setConfigId(trade.getConfigId());
            refund.setDataId(model.getDataId());
            refund.setDataType(model.getDataType());
            refund.setFee(model.getFee());
            refund.setRequestInfo(JSONObject.toJSONString(params));
            refund.setResponseInfo(JSONObject.toJSONString(respMap));
            refund.setTradeStatus(Integer.valueOf(1));
            refund.setTradeType(Integer.valueOf(2));
            refund.setUserId(this.userSessionManager.getUserId());
            this.weixinTradeRepository.save(refund);
            return Result.ok();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.failed(4004,e.getMessage());
        }

    }

    @Transactional(rollbackFor = {Exception.class})
    public Result<WeixinRefundNotifyView> getWeixinRefundNotifyDataAndValidateAndUpdateWeixinTrade(Long configId) {
        Map<String, String> aesMap = new HashMap<>();
        Map<String, String> notifyData = this.weixinNotifyUtil.getNotifyData();
        String req_info = notifyData.get("req_info");
        WeixinConfig weixinConfig = this.weixinConfigRepository.findById(configId).get();
        try {
            byte[] b = Base64Utils.decodeFromString(req_info);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            String encodeKey = WXPayUtil.MD5(weixinConfig.getPayKey()).toLowerCase();
            SecretKeySpec key = new SecretKeySpec(encodeKey.getBytes(), "AES");
            cipher.init(2, key);
            String newstr = new String(cipher.doFinal(b));
            aesMap = WXPayUtil.xmlToMap(newstr);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.failed(1004,"解密数据时失败");
        }
        if ("success".equalsIgnoreCase(notifyData.get("return_code"))) {
            String weixinTradeId = aesMap.get("refund_id");
            String refundId = aesMap.get("out_refund_no");
            WeixinTrade trade = this.weixinTradeRepository.findById(Long.valueOf(refundId)).get();
            trade.setWeixinTradeId(weixinTradeId);
            trade.setTradeStatus(Integer.valueOf(2));
            trade.setNotifyInfo(JSONObject.toJSONString(aesMap));
            this.weixinTradeRepository.saveAndFlush(trade);
            WeixinRefundNotifyView view = new WeixinRefundNotifyView();
            view.setDataId(trade.getDataId());
            view.setNotifyParam(aesMap);
            return Result.ok(view,"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
        } else {
            return Result.failed(1005,"微信参数错误");
        }
    }


    private String convertNotifyUrl(String notifyUrl, String... pathVariable) {
        //支付回调（wx） https://xhyp.quexiaoer.cn/api
        String domainPay = "";
        //获取配置的认养订单超时分钟,30
        Optional<ValueConfig> valueConfig = valueConfigRepository.findById("pay_notify_domain_weixin");
        if (valueConfig.isPresent() && StringUtils.isNotBlank(valueConfig.get().getValue())){
            domainPay = valueConfig.get().getValue();
        }
        StringBuilder url = new StringBuilder();
        url.append(domainPay).append(notifyUrl);
        if (null != pathVariable) {
            String seperateChar = "/";
            for (String path : pathVariable) {
                if (StringUtils.isNotBlank(path)) {
                    url.append("/").append(path);
                }
            }
        }
        return url.toString();
    }

}
